home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
-
- /* $Header: b2gen.c,v 1.4 85/08/27 10:57:31 timo Exp $ */
-
- /* Code generation */
-
- #include "b.h"
- #include "b0fea.h"
- #include "b1obj.h"
- #include "b2exp.h"
- #include "b2nod.h"
- #include "b2gen.h" /* Must be after b2nod.h */
- #include "b3err.h"
- #include "b3env.h"
- #include "b3int.h"
- #include "b3sem.h"
- #include "b3sou.h"
-
- Visible Procedure fix_nodes(pt, code) parsetree *pt; parsetree *code; {
- context c; value *setup(), *su;
- sv_context(&c);
- curline= *pt; curlino= one;
- su= setup(*pt);
- if (su) analyze(*pt, su);
- curline= *pt; curlino= one;
- inithreads();
- fix(pt, su ? 'x' : 'v');
- endthreads(code);
- cleanup();
- #ifdef TYPE_CHECK
- if (cntxt != In_prmnv) type_check(*pt);
- #endif
- set_context(&c);
- }
-
- /* ******************************************************************** */
-
- /* Utilities used by threading. */
-
- /* A 'threaded tree' is, in our case, a fixed(*) parse tree with extra links
- that are used by the interpreter to determine the execution order.
- __________
- (*) 'Fixed' means: processed by 'fix_nodes', which removes UNPARSED
- nodes and distinguishes TAG nodes into local, global tags etc.
- fix_nodes also creates the threads, but this is accidental, not
- essential. For UNPARSED nodes, the threads are actually laid
- in a second pass through the subtree that was UNPARSED.
- __________
-
- A small example: the parse tree for the expression 'a+b*c' looks like
-
- (DYOP,
- (TAGlocal, "a"),
- "+",
- (DYOP,
- (TAGlocal, "b"),
- "*",
- (TAGlocal, "c"))).
-
- The required execution order is here:
-
- 1) (TAGlocal, "a")
- 2) (TAGlocal, "b")
- 3) (TAGlocal, "c")
- 4) (DYOP, ..., "*", ...)
- 5) (DYOP, ..., "+", ...)
-
- Of course, the result of each operation (if it has a result) is pushed
- on a stack, and the operands are popped from this same stack. Think of
- reversed polish notation (well-known by owners of HP pocket calculators).
-
- The 'threads' are explicit links from each node to its successor in this
- execution order. Conditional operations like IF and AND have two threads,
- one for success and one for failure. Loops can be made by having the
- thread from the last node of the loop body point to the head of the loop.
-
- Threading expressions, locations and simple-commands is easy: recursively
- thread each of the subtrees, then lay a thread from the last threaded
- to the current node. Nodes occurring in a 'location' context are
- marked, so that the interpreter knows when to push a 'location' on
- the stack.
-
- Tests and looping commands cause most of the complexity of the threading
- utilities. The basic technique is 'backpatching'.
- Nodes that need a conditional forward jump are chained together in a
- linked list, and when their destination is reached, all nodes in the
- chain get its 'address' patched into their secondary thread. There is
- one such chain, called 'bpchain', which at all times contains those nodes
- whose secondary destination would be the next generated instruction.
- This is used by IF, WHILE, test-suites, AND and OR.
-
- To generate a loop, both this chain and the last normal instruction
- (if any) are diverted to the node where the loop continues.
-
- For test-suites, we also need to be capable of jumping unconditionally
- forward (over the remainder of the SELECT-command). This is done by
- saving both the backpatch chain and the last node visited, and restoring
- them after the remainder has been processed.
- */
-
- /* Implementation tricks: in order not to show circular lists to 'release',
- parse tree nodes are generated as compounds where there is room for two
- more fields than their length indicates.
- */
-
- #define Flag (MkSmallInt(1))
- /* Flag used to indicate Location or TestRefinement node */
-
- Hidden parsetree start; /* First instruction. Picked up by endthreads() */
-
- Hidden parsetree last; /* Last visited node */
-
- Hidden parsetree bpchain; /* Backpatch chain for conditional goto's */
- Hidden parsetree *wanthere; /* Chain of requests to return next tree */
-
- extern string opcodes[];
-
-
- /* Start threading */
-
- Hidden Procedure inithreads() {
- bpchain= NilTree;
- wanthere= 0;
- last= 0;
- here(&start);
- }
-
- /* Finish threading */
-
- Hidden Procedure endthreads(code) parsetree *code; {
- jumpto(Stop);
- if (!still_ok) start= NilTree;
- *code= start;
- }
-
-
- /* Fill 't' as secondary thread for all nodes in the backpatch chain,
- leaving the chain empty. */
-
- Hidden Procedure backpatch(t) parsetree t; {
- parsetree u;
- while (bpchain != NilTree) {
- u= Thread2(bpchain);
- Thread2(bpchain)= t;
- bpchain= u;
- }
- }
-
- Visible Procedure jumpto(t) parsetree t; {
- parsetree u;
- if (!still_ok) return;
- while (wanthere != 0) {
- u= *wanthere;
- *wanthere= t;
- wanthere= (parsetree*)u;
- }
- while (last != NilTree) {
- u= Thread(last);
- Thread(last)= t;
- last= u;
- }
- backpatch(t);
- }
-
- Hidden parsetree seterr(n) int n; {
- return (parsetree)MkSmallInt(n);
- }
-
- /* Visit node 't', and set its secondary thread to 't2'. */
-
- Hidden Procedure visit2(t, t2) parsetree t, t2; {
- if (!still_ok) return;
- jumpto(t);
- Thread2(t)= t2;
- #ifdef DEBUG
- fprintf(stderr, "\tvisit %s %s\n", opcodes[Nodetype(t)],
- t2 == NilTree ? "" : "[*]");
- #endif DEBUG
- Thread(t)= NilTree;
- last= t;
- }
-
- /* Visit node 't' */
-
- Hidden Procedure visit(t) parsetree t; {
- visit2(t, NilTree);
- }
-
- /* Visit node 't' and flag it as a location (or test-refinement). */
-
- Hidden Procedure lvisit(t) parsetree t; {
- visit2(t, Flag);
- }
-
- #ifdef NOT_USED
- Hidden Procedure jumphere(t) parsetree t; {
- Thread(t)= last;
- last= t;
- }
- #endif
-
- /* Add node 't' to the backpatch chain. */
-
- Hidden Procedure jump2here(t) parsetree t; {
- if (!still_ok) return;
- Thread2(t)= bpchain;
- bpchain= t;
- }
-
- Hidden Procedure here(pl) parsetree *pl; {
- if (!still_ok) return;
- *pl= (parsetree) wanthere;
- wanthere= pl;
- }
-
- Visible Procedure hold(pl) struct state *pl; {
- if (!still_ok) return;
- pl->h_last= last; pl->h_bpchain= bpchain; pl->h_wanthere= wanthere;
- last= bpchain= NilTree; wanthere= 0;
- }
-
- Visible Procedure let_go(pl) struct state *pl; {
- parsetree p, *w;
- if (!still_ok) return;
- if (last) {
- for (p= last; Thread(p) != NilTree; p= Thread(p))
- ;
- Thread(p)= pl->h_last;
- }
- else last= pl->h_last;
- if (bpchain) {
- for (p= bpchain; Thread2(p) != NilTree; p= Thread2(p))
- ;
- Thread2(p)= pl->h_bpchain;
- }
- else bpchain= pl->h_bpchain;
- if (wanthere) {
- for (w= wanthere; *w != 0; w= (parsetree*) *w)
- ;
- *w= (parsetree) pl->h_wanthere;
- }
- else wanthere= pl->h_wanthere;
- }
-
- Hidden bool reachable() {
- return last != NilTree || bpchain != 0 || wanthere != 0;
- }
-
-
- /* ******************************************************************** */
- /* *********************** code generation **************************** */
- /* ******************************************************************** */
-
- Forward bool is_variable();
- Forward bool is_cmd_ref();
- Forward value copydef();
-
- Visible Procedure fix(pt, flag) parsetree *pt; char flag; {
- struct state st; value v, function; parsetree t, l1= NilTree;
- typenode nt; string s; char c; int n, k, len;
-
- t= *pt;
- if (!Is_node(t) || !still_ok) return;
- nt= Nodetype(t);
- if (nt < 0 || nt >= NTYPES) syserr(MESS(2200, "fix bad tree"));
- s= gentab[nt];
- if (s == NULL) return;
- n= First_fieldnr;
- if (flag == 'x') curline= t;
- while ((c= *s++) != '\0' && still_ok) {
- switch (c) {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- n= (c - '0') + First_fieldnr;
- break;
- case 'c':
- v= *Branch(t, n);
- if (v != Vnil) {
- len= Nfields(v);
- for (k= 0; k < len; ++k)
- fix(Field(v, k), flag);
- }
- ++n;
- break;
- case '#':
- curlino= *Branch(t, n);
- ++n;
- break;
- case 'g':
- case 'h':
- ++n;
- break;
- case 'a':
- case 'l':
- if (flag == 'v' || flag == 't')
- c= flag;
- /* Fall through */
- case '!':
- case 't':
- case 'u':
- case 'v':
- case 'x':
- fix(Branch(t, n), c);
- ++n;
- break;
- case 'f':
- f_fpr_formals(*Branch(t, n));
- ++n;
- break;
-
- case '?':
- if (flag == 'v')
- f_eunparsed(pt);
- else if (flag == 't')
- f_cunparsed(pt);
- else
- syserr(MESS(2201, "fix unparsed with bad flag"));
- fix(pt, flag);
- break;
- case 'C':
- v= *Branch(t, REL_LEFT);
- if (Comparison(Nodetype(v)))
- jump2here(v);
- break;
- case 'D':
- v= (value)*Branch(t, DYA_NAME);
- if (!is_dyafun(v, &function))
- fixerr2(v, MESS(2202, " isn't a dyadic function"));
- else
- *Branch(t, DYA_FCT)= copydef(function);
- break;
- case 'E':
- v= (value)*Branch(t, DYA_NAME);
- if (!is_dyaprd(v, &function))
- fixerr2(v, MESS(2203, " isn't a dyadic predicate"));
- else
- *Branch(t, DYA_FCT)= copydef(function);
- break;
- case 'G':
- jumpto(l1);
- break;
- case 'H':
- here(&l1);
- break;
- case 'I':
- if (*Branch(t, n) == NilTree)
- break;
- /* Else fall through */
- case 'J':
- jump2here(t);
- break;
- case 'K':
- hold(&st);
- break;
- case 'L':
- let_go(&st);
- break;
- case 'M':
- v= (value)*Branch(t, MON_NAME);
- if (is_variable(v) || !is_monfun(v, &function))
- fixerr2(v, MESS(2204, " isn't a monadic function"));
- else
- *Branch(t, MON_FCT)= copydef(function);
- break;
- case 'N':
- v= (value)*Branch(t, MON_NAME);
- if (is_variable(v) || !is_monprd(v, &function))
- fixerr2(v, MESS(2205, " isn't a monadic predicate"));
- else
- *Branch(t, MON_FCT)= copydef(function);
- break;
- #ifdef REACH
- case 'R':
- if (*Branch(t, n) != NilTree && !reachable())
- fixerr(MESS(2206, "command cannot be reached"));
- break;
- #endif
- case 'S':
- jumpto(Stop);
- break;
- case 'T':
- if (flag == 't')
- f_ctag(pt);
- else if (flag == 'v' || flag == 'x')
- f_etag(pt);
- else
- f_ttag(pt);
- break;
- case 'U':
- f_ucommand(pt);
- break;
- case 'V':
- visit(t);
- break;
- case 'X':
- if (flag == 'a' || flag == 'l' || flag == '!')
- lvisit(t);
- else
- visit(t);
- break;
- case 'W':
- /*!*/ visit2(t, seterr(1));
- break;
- case 'Y':
- if (still_ok && reachable()) {
- if (nt == YIELD)
- fixerr(MESS(2207, "YIELD-unit returns no value"));
- else
- fixerr(MESS(2208, "TEST-unit reports no outcome"));
- }
- break;
- case 'Z':
- if (!is_cmd_ref(t) && still_ok && reachable())
- fixerr(MESS(2209, "refinement returns no value c.q. reports no outcome"));
- *Branch(t, REF_START)= copy(l1);
- break;
- }
- }
- }
-
- /* ******************************************************************** */
-
- Hidden bool is_cmd_ref(t) parsetree t; { /* HACK */
- value name= *Branch(t, REF_NAME);
- string s= strval(name);
- /* return isupper(*s); */
- return *s <= 'Z' && *s >= 'A';
- }
-
- Visible value copydef(f) value f; {
- funprd *fpr= Funprd(f);
- if (fpr->pre == Use) return Vnil;
- return copy(f);
- }
-
- Hidden bool is_basic_target(v) value v; {
- return envassoc(formals, v) ||
- locals != Vnil && envassoc(locals, v) ||
- envassoc(globals, v) ||
- envassoc(mysteries, v);
- }
-
- Hidden bool is_variable(v) value v; {
- value f;
- return is_basic_target(v) ||
- envassoc(refinements, v) ||
- is_zerfun(v, &f);
- }
-
- Hidden bool is_target(p) parsetree p; {
- value v= *Branch(p, First_fieldnr); int k, len;
- switch (Nodetype(p)) {
-
- case TAG:
- return is_basic_target(v);
-
- case SELECTION:
- case BEHEAD:
- case CURTAIL:
- case COMPOUND:
- return is_target(v);
-
- case COLLATERAL:
- len= Nfields(v);
- k_Overfields {
- if (!is_target(*Field(v, k))) return No;
- }
- return Yes;
-
- default:
- return No;
-
- }
- }
-
- /* ******************************************************************** */
-
- Hidden Procedure f_actuals(formals, pactuals) parsetree formals, *pactuals; {
- /* name, actual, next */
- value actuals= *pactuals, act, form, next_a, next_f, kw, *pact;
- kw= *Branch(actuals, ACT_KEYW);
- pact= Branch(actuals, ACT_EXPR); act= *pact;
- form= *Branch(formals, FML_TAG);
- next_a= *Branch(actuals, ACT_NEXT); next_f= *Branch(formals, FML_NEXT);
- if (compare(*Branch(formals, FML_KEYW), kw) != 0)
- fixerr3(MESS(2210, "wrong keyword "), kw, 0);
- else if (act == Vnil && form != Vnil)
- fixerr3(MESS(2211, "missing actual after "), kw, 0);
- else if (next_a == Vnil && next_f != Vnil)
- fixerr3(MESS(2212, "can't find expected "),
- *Branch(next_f, FML_KEYW), 0);
- else if (act != Vnil && form == Vnil)
- fixerr3(MESS(2213, "unexpected actual after "), kw, 0);
- else if (next_a != Vnil && next_f == Vnil)
- fixerr3(MESS(2214, "unexpected keyword "),
- *Branch(next_a, ACT_KEYW), 0);
- else {
- if (act != Vnil) {
- parsetree st; struct state save;
- hold(&save); here(&st);
- if (is_target(act)) f_targ(pact);
- else f_expr(pact);
- jumpto(Stop); let_go(&save);
- *Branch(actuals, ACT_START)= copy(st);
- }
- if (still_ok && next_a != Vnil)
- f_actuals(next_f, Branch(actuals, ACT_NEXT));
- }
- }
-
- Hidden Procedure f_ucommand(pt) parsetree *pt; {
- value t= *pt, *aa;
- parsetree u, *f1= Branch(t, UCMD_NAME), *f2= Branch(t, UCMD_ACTUALS);
- release(*Branch(t, UCMD_DEF));
- *Branch(t, UCMD_DEF)= Vnil;
- if ((aa= envassoc(refinements, *f1)) != Pnil) {
- if (*Branch(*f2, ACT_EXPR) != Vnil
- || *Branch(*f2, ACT_NEXT) != Vnil)
- fixerr(MESS(2215, "refinement with parameters"));
- else *Branch(t, UCMD_DEF)= copy(*aa);
- }
- else if (is_unit(*f1, How, &aa)) {
- u= How_to(*aa)->unit;
- f_actuals(*Branch(u, HOW_FORMALS), f2);
- }
- else if (still_ok)
- fixerr3(MESS(2216, "you haven't told me HOW'TO "), *f1, 0);
- }
-
- Hidden Procedure f_fpr_formals(t) parsetree t; {
- switch (Nodetype(t)) {
- case TAG:
- break;
- case MONF: case MONPRD:
- f_targ(Branch(t, MON_RIGHT));
- break;
- case DYAF: case DYAPRD:
- f_targ(Branch(t, DYA_LEFT));
- f_targ(Branch(t, DYA_RIGHT));
- break;
- default:
- syserr(MESS(2217, "f_fpr_formals"));
- }
- }
-
- Visible bool modify_tag(name, tag) parsetree *tag; value name; {
- value *aa, function;
- *tag= NilTree;
- if (aa= envassoc(formals, name))
- *tag= node3(TAGformal, name, copy(*aa));
- else if (locals != Vnil && (aa= envassoc(locals, name)))
- *tag= node3(TAGlocal, name, copy(*aa));
- else if (aa= envassoc(globals, name))
- *tag= node2(TAGglobal, name);
- else if (aa= envassoc(mysteries, name))
- *tag= node3(TAGmystery, name, copy(*aa));
- else if (aa= envassoc(refinements, name))
- *tag= node3(TAGrefinement, name, copy(*aa));
- else if (is_zerfun(name, &function))
- *tag= node3(TAGzerfun, name, copydef(function));
- else if (is_zerprd(name, &function))
- *tag= node3(TAGzerprd, name, copydef(function));
- else return No;
- return Yes;
- }
-
- Hidden Procedure f_etag(pt) parsetree *pt; {
- parsetree t= *pt; value name= copy(*Branch(t, TAG_NAME));
- if (modify_tag(name, &t)) {
- release(*pt);
- *pt= t;
- if (Nodetype(t) == TAGzerprd)
- fixerr2(name, MESS(2218, " cannot be used in an expression"));
- else
- visit(t);
- } else {
- fixerr2(name, MESS(2219, " has not yet received a value"));
- release(name);
- }
- }
-
- Hidden Procedure f_ttag(pt) parsetree *pt; {
- parsetree t= *pt; value name= copy(*Branch(t, TAG_NAME));
- if (modify_tag(name, &t)) {
- release(*pt);
- *pt= t;
- switch (Nodetype(t)) {
- case TAGrefinement:
- fixerr(MESS(2220, "a refinement may not be used as a target"));
- break;
- case TAGzerfun:
- case TAGzerprd:
- fixerr2(name, MESS(2221, " hasn't been initialised or defined"));
- break;
- default:
- lvisit(t);
- break;
- }
- } else {
- fixerr2(name, MESS(2222, " hasn't been initialised or defined"));
- release(name);
- }
- }
-
- Hidden Procedure f_ctag(pt) parsetree *pt; {
- parsetree t= *pt; value name= copy(*Branch(t, TAG_NAME));
- if (modify_tag(name, &t)) {
- release(*pt);
- *pt= t;
- switch (Nodetype(t)) {
- case TAGrefinement:
- lvisit(t); /* 'Loc' flag here means 'Test' */
- break;
- case TAGzerprd:
- visit(t);
- break;
- default:
- fixerr2(name, MESS(2223, " is neither a refined test nor a zeroadic predicate"));
- break;
- }
- } else {
- fixerr2(name, MESS(2224, " is neither a refined test nor a zeroadic predicate"));
- release(name);
- }
- }
-